Documentation for Users  2.2.0
Perception Toolbox for Virtual Reality (PTVR) Manual
visual_search.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 
3 '''
4 ...\PTVR_Researchers\Python_Scripts\Experiments\visual_search.py
5 
6 
7 Important note:
8  When using a pointer, it is necessary to specify your headset's manufacturer:
9  headset_manufacturer = "Vive" (default) refers to the HTC Vive pro series headsets.
10  headset_manufacturer = "Oculus" refers to the Quest 1, 2 or 3 headsets (in keeping
11  with OpenXR terminology).
12  For example:
13 my_world = The3DWorld (
14  headset_manufacturer = "Vive" # "Vive" by default
15 
16 
17 '''
18 from PTVR.Visual import The3DWorld# Used to create the experiment
19 from PTVR.SystemUtils import LaunchThe3DWorld # Used to launch the experiment
20 from PTVR.Stimuli.Scenes import VisualScene, CheckScene # Used to create the scene
21 from PTVR.Stimuli.Objects import Cube, Sphere, AudioSource # Used to create visual object
22 from PTVR.Stimuli.Color import RGBColor
23 from PTVR.Data.Event import PointedAt, HandController
24 from PTVR.Data.Callback import AddInteractionPassedToCallback, EndCurrentScene,ChangeObjectOutline,ChangeAudioSettings
25 from PTVR.Pointing.PointingCursor import PointingLaser, LaserContingency
26 from numpy import array
27 import random
28 
29 
30 # =============================================================================
31 # PARAMETERS #
32 # =============================================================================
33 username = "Henry" # The subject's name
34 height_of_head = 1.2 # this value (in meters) is chosen so as to correspond
35 # to the height of the subject's headset during the experiment (seated or standing)
36 viewpoint_position = array ([0, height_of_head, 0])
37 # Recall: a viewpoint is the origin of the current CS used to draw
38 # the stimuli to be "seen" from this viewpoint
39 
40 
41 
42 number_of_distractor = 512
43 
44 radial_distance_min_max_values = array([2, 15])
45 
46 # Color VisualSearch
47 color_base = RGBColor(r=0, g=1, b=0) # Green
48 color_distractor = RGBColor(r=1, g=0, b=0) # Red
49 color_target = RGBColor(r=1, g=0, b=0) # Red
50 
51 
52 my_laser_color = RGBColor(r= 0.7)
53 hand = LaserContingency.RIGHT_HAND
54 
55 
56 
57 # =============================================================================
58 # END PARAMETERS #
59 # =============================================================================
60 my_world = The3DWorld(
61  headset_manufacturer = "Vive" # "Vive" by default
62  )
63 # When using a pointer, it is necessary to specify your headset's manufacturer:
64 # headset_manufacturer = "Vive" (default) refers to the HTC Vive pro series headsets.
65 # headset_manufacturer = "Oculus" refers to the Quest 1, 2 or 3 headsets (in keeping
66 # with OpenXR terminology
67 
68 
69 sound_end_of_block = "applaudisement.mp3" # sound indicating end of block
70 sound_wrong_response = "buzzer.ogg"
71 handcontroller_id = my_world.handControllerRight.id
72 configurations_set = set()
73 
74 def new_objects(random_value):
75  if(random_value == 1):
76  value = random.randint (0, 1)
77  if(value == 1):
78  color_object = color_base
79  else:
80  color_object = color_distractor
81 
82  my_object = Sphere(size_in_meters= array([0.2, 0.2, 0.2]), color = color_object)
83  else:
84  my_object = Cube(size_in_meters= array([0.2, 0.2, 0.2]), color = color_base)
85  return my_object
86 
88  reticle_pointing_at_object = PointedAt (target_id = my_object.id,
89  activation_cone_origin_id=handcontroller_id,
90  activation_cone_radius_deg=1, activation_cone_depth=500, mode="press",
91  event_name="object_is_pointed_at")
92  reticle_NOT_pointing_at_object = PointedAt (target_id = my_object.id,
93  activation_cone_origin_id=handcontroller_id,
94  activation_cone_radius_deg=1, activation_cone_depth=500, mode="release",
95  event_name="object_is_not_pointed_at")
96  return reticle_pointing_at_object, reticle_NOT_pointing_at_object
97 
99  callback_outline_on = ChangeObjectOutline(object_id=my_object.id, outline_color=RGBColor(r=1.0,g= 1.0, b=1.0,a= 1),
100  outline_width=5,
101  effect="activate")
102  callback_outline_off = ChangeObjectOutline(object_id=my_object.id, outline_color=RGBColor(r=1.0,g= 1.0, b=1.0,a= 1),
103  effect="deactivate")
104  return callback_outline_on,callback_outline_off
105 
106 def main():
107  print("Creating an experiment to study visual search in various 3D regions...")
108 
109  my_initial_scene = CheckScene ( my_world = my_world,
110  are_both_hand_controllers_visible= True,
111  text_information = "In this experiment you have "\
112  "to search for the red cube \n among over objects " \
113  "by pointing at it and pressing the trigger.\n" \
114  "When you are ready to start, please press the trigger.")
115  my_world.add_scene (my_initial_scene)
116 
117  # Create the visual scene that will contain the distractors and the target
118  my_scene = VisualScene (is_right_hand_controller_visible = True)
119 
120  # Create a pointer
121  my_laser_beam = PointingLaser ( laser_hand = hand,
122  laser_color = my_laser_color,
123  laser_width = 0.01 )
124  my_scene.place_pointing_laser ( my_laser_beam )
125  my_scene.AddPointingCursor(my_laser_beam)
126 
127 
128  my_world.translate_coordinate_system_along_current(translation = viewpoint_position)
129 
130  audio_wrong = AudioSource(audio_file=sound_wrong_response, is_playing_directly=False)
131  my_scene.place(audio_wrong,my_world)
132  while len(configurations_set) < number_of_distractor+1:
133 
134  radialdistance = random.uniform(radial_distance_min_max_values [0],
135  radial_distance_min_max_values [1])
136  eccentricity = random.gauss(90, 40)
137  halfmeridian = random.randint(0, 360)
138  configuration = (radialdistance, eccentricity, halfmeridian)
139  if tuple(configuration) not in configurations_set:
140  configurations_set.add(tuple(configuration))
141 
142  configurations = [list(config) for config in configurations_set]
143  target_configuration = random.choice(configurations)
144  print("my target is at np.array([radial_distance,eccentricity,half_meridian)) : " +
145  str(target_configuration))
146  for config in configurations:
147  radialdistance = config[0]
148  eccentricity = config[1]
149  halfmeridian = config[2]
150  if(config != target_configuration):
151  my_object = new_objects(random.randint(0, 1)) # Sphere or Cube
152  else:
153  my_object = Cube(size_in_meters= array([0.2, 0.2, 0.2]), color=color_target)
154  my_object.set_perimetric_coordinates(radialDistance=radialdistance,
155  eccentricity=eccentricity, halfMeridian=halfmeridian)
156  my_scene.place(my_object, my_world)
157  inside,outside = create_events_when_pointing_at_objects (my_object)
158  outline_on,outline_off = create_callbacks_to_change_objects(my_object)
159  launchwrong_song = ChangeAudioSettings(audio_id=audio_wrong.id)
160  press = HandController(valid_responses=['right_trigger'],mode="press",event_name="press")
161  if(config != target_configuration):
162  add = AddInteractionPassedToCallback(events=[press], callbacks=[launchwrong_song],
163  effect="activate")
164  remove = AddInteractionPassedToCallback(events=[press], callbacks=[launchwrong_song],
165  effect="deactivate")
166  my_scene.AddInteraction(events=[inside], callbacks=[add,outline_on])
167  my_scene.AddInteraction(events=[outside], callbacks=[remove,outline_off])
168 
169  else:
170  finish = EndCurrentScene(callback_name = "target has been found")
171 
172  add = AddInteractionPassedToCallback(events=[press], callbacks=[finish], effect="activate")
173  remove = AddInteractionPassedToCallback(events=[press], callbacks=[finish], effect="deactivate")
174  # Register Events and Callbacks
175  my_scene.AddInteraction(events=[inside], callbacks=[add,outline_on])
176  my_scene.AddInteraction(events=[outside], callbacks=[remove,outline_off])
177 
178 
179  my_world.add_scene(my_scene)
180 
181  my_world.reset_coordinate_system()
182 
183  calibration = CheckScene(my_world = my_world,text_information= "Congratulations!",are_both_hand_controllers_visible = True)
184  audio = AudioSource(audio_file=sound_end_of_block)
185  calibration.place(audio,my_world)
186  my_world.add_scene(calibration)
187 
188  my_world.write() # create json file containing the parameters of the present experiment
189  print("The .json file corresponding to this experiment has been created.")
190 
191 
192 if __name__ == "__main__":
193  main()
194  LaunchThe3DWorld(username) # Launch the3DWorld with PTVR.
def LaunchThe3DWorld(jsonFileCategory="Externals")
Definition: SystemUtils.py:182
def new_objects(random_value)
def create_events_when_pointing_at_objects(my_object)
def create_callbacks_to_change_objects(my_object)